<?php

/**
 * 农历计算 [支持范围: 1950 - 2050]
 *
 * @author suning <snsnsky@gmail.com>
 *
 * $Id$
 */

namespace Tools;

class Lunar
{
    /**
     * 公历月份日
     *
     * @var array
     */
    private static $gg_day = [1 => 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    /**
     * 农历起始日期
     *
     * @var int
     */
    private static $ln_start = 1950;

    /**
     * 该年的农历正月初一到该年公历1月1日的天数；1~12：农历月份天数；闰：如有闰月，记录该月平月天数
     *
     * @var array
     */
    private static $ln_day = [
        [47, 29, 30, 30, 29, 30, 30, 29, 29, 30, 29, 30, 29],
        [36, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 30],
        [6, 29, 30, 29, 30, 59, 29, 30, 30, 29, 30, 29, 30, 29],
        [44, 29, 30, 29, 29, 30, 30, 29, 30, 30, 29, 30, 29],
        [33, 30, 29, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30],
        [23, 29, 30, 59, 29, 29, 30, 29, 30, 29, 30, 30, 30, 29],
        [42, 29, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30],
        [30, 30, 29, 30, 29, 30, 29, 29, 59, 30, 29, 30, 29, 30],
        [48, 30, 30, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30],
        [38, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29],
        [27, 30, 29, 30, 29, 30, 59, 30, 29, 30, 29, 30, 29, 30],
        [45, 30, 29, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30],
        [35, 29, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30, 29],
        [24, 30, 29, 30, 58, 30, 29, 30, 29, 30, 30, 30, 29, 29],
        [43, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30, 30],
        [32, 29, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29],
        [20, 30, 30, 59, 30, 29, 29, 30, 29, 29, 30, 30, 29, 30],
        [39, 30, 30, 29, 30, 30, 29, 29, 30, 29, 30, 29, 30],
        [29, 29, 30, 29, 30, 30, 29, 59, 30, 29, 30, 29, 30, 30],
        [47, 29, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29],
        [36, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30, 29, 30],
        [26, 29, 30, 29, 29, 59, 30, 29, 30, 30, 30, 29, 30, 30],
        [45, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30, 29, 30],
        [33, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29, 30],
        [22, 30, 30, 29, 59, 29, 30, 29, 29, 30, 30, 29, 30, 30],
        [41, 30, 30, 29, 30, 29, 29, 30, 29, 29, 30, 29, 30],
        [30, 30, 30, 29, 30, 29, 30, 29, 59, 29, 30, 29, 30, 30],
        [48, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29],
        [37, 30, 29, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29],
        [27, 30, 29, 29, 30, 29, 60, 29, 30, 30, 29, 30, 29, 30],
        [46, 30, 29, 29, 30, 29, 30, 29, 30, 30, 29, 30, 30],
        [35, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29, 30, 30],
        [24, 30, 29, 30, 58, 30, 29, 29, 30, 29, 30, 30, 30, 29],
        [43, 30, 29, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30],
        [32, 30, 29, 30, 30, 29, 29, 30, 29, 29, 59, 30, 30, 30],
        [50, 29, 30, 30, 29, 30, 29, 30, 29, 29, 30, 29, 30],
        [39, 29, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 29],
        [28, 30, 29, 30, 29, 30, 59, 30, 30, 29, 30, 29, 29, 30],
        [47, 30, 29, 30, 29, 30, 29, 30, 30, 29, 30, 30, 29],
        [36, 30, 29, 29, 30, 29, 30, 29, 30, 29, 30, 30, 30],
        [26, 29, 30, 29, 29, 59, 29, 30, 29, 30, 30, 30, 30, 30],
        [45, 29, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30, 30],
        [34, 29, 30, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30],
        [22, 29, 30, 59, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30],
        [40, 30, 30, 30, 29, 30, 29, 30, 29, 29, 30, 29, 30],
        [30, 29, 30, 30, 29, 30, 29, 30, 59, 29, 30, 29, 30, 30],
        [49, 29, 30, 29, 30, 30, 29, 30, 29, 30, 30, 29, 29],
        [37, 30, 29, 30, 29, 30, 29, 30, 30, 29, 30, 30, 29],
        [27, 30, 29, 29, 30, 58, 30, 30, 29, 30, 30, 29, 30, 29],
        [46, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30, 30, 29],
        [35, 30, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30, 29],
        [23, 30, 30, 29, 59, 30, 29, 29, 30, 29, 30, 29, 30, 30],
        [42, 30, 30, 29, 30, 29, 30, 29, 29, 30, 29, 30, 29],
        [31, 30, 30, 29, 30, 30, 29, 30, 29, 29, 30, 29, 30],
        [21, 29, 59, 30, 30, 29, 30, 29, 30, 29, 30, 29, 30, 30],
        [39, 29, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29],
        [28, 30, 29, 30, 29, 30, 29, 59, 30, 30, 29, 30, 30, 30],
        [48, 29, 29, 30, 29, 29, 30, 29, 30, 30, 30, 29, 30],
        [37, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30, 29, 30],
        [25, 30, 30, 29, 29, 59, 29, 30, 29, 30, 29, 30, 30, 30],
        [44, 30, 29, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30],
        [33, 30, 29, 30, 30, 29, 30, 29, 29, 30, 29, 30, 29],
        [22, 30, 29, 30, 59, 30, 29, 30, 29, 30, 29, 30, 29, 30],
        [40, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30],
        [30, 29, 30, 29, 30, 29, 30, 29, 30, 59, 30, 29, 30, 30],
        [49, 29, 30, 29, 29, 30, 29, 30, 30, 30, 29, 30, 29],
        [38, 30, 29, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30],
        [27, 29, 30, 29, 30, 29, 59, 29, 30, 29, 30, 30, 30, 29],
        [46, 29, 30, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30],
        [35, 30, 29, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30],
        [24, 29, 30, 30, 59, 30, 29, 29, 30, 29, 30, 29, 30, 30],
        [42, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29],
        [31, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30],
        [21, 29, 59, 29, 30, 30, 29, 30, 30, 29, 30, 29, 30, 30],
        [40, 29, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30, 29],
        [28, 30, 29, 30, 29, 29, 59, 30, 29, 30, 30, 30, 29, 30],
        [47, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30, 30, 29],
        [36, 30, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29],
        [25, 30, 30, 30, 29, 59, 29, 30, 29, 29, 30, 30, 29, 30],
        [43, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29, 30, 30],
        [33, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29],
        [22, 29, 30, 59, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30],
        [41, 30, 29, 29, 30, 29, 30, 30, 29, 30, 30, 29, 30],
        [30, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30, 59, 30, 30],
        [49, 29, 30, 29, 29, 30, 29, 30, 29, 30, 30, 29, 30],
        [38, 30, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29, 30],
        [27, 30, 30, 29, 30, 29, 59, 29, 29, 30, 29, 30, 30, 29],
        [45, 30, 30, 29, 30, 29, 29, 30, 29, 29, 30, 29, 30],
        [34, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29, 30, 29],
        [23, 30, 30, 29, 30, 59, 30, 29, 30, 29, 30, 29, 29, 30],
        [42, 30, 29, 30, 30, 29, 30, 29, 30, 30, 29, 30, 29],
        [31, 29, 30, 29, 30, 29, 30, 30, 29, 30, 30, 29, 30],
        [21, 29, 59, 29, 30, 29, 30, 29, 30, 30, 29, 30, 30, 30],
        [40, 29, 30, 29, 29, 30, 29, 29, 30, 30, 29, 30, 30],
        [29, 30, 29, 30, 29, 29, 30, 58, 30, 29, 30, 30, 30, 29],
        [47, 30, 29, 30, 29, 29, 30, 29, 29, 30, 29, 30, 30],
        [36, 30, 29, 30, 29, 30, 29, 30, 29, 29, 30, 29, 30],
        [25, 30, 29, 30, 30, 59, 29, 30, 29, 29, 30, 29, 30, 29],
        [44, 29, 30, 30, 29, 30, 30, 29, 30, 29, 29, 30, 29],
        [32, 30, 29, 30, 29, 30, 30, 29, 30, 30, 29, 30, 29],
        [22, 29, 30, 59, 29, 30, 29, 30, 30, 29, 30, 30, 29, 29],
    ];


    /**
     * 公历转农历
     *
     * @param string $date 日期, 2012-05-03
     * @param bool $time 是否返回时间戳, false: 农历字符串, true: 是
     * @return bool|int|string
     */
    public static function GG2LN($date, $time = false)
    {
        list($year, $month, $day) = explode("-", $date);
        if ($year <= 1951 || $month <= 0 || $day <= 0 || $year >= 2051) {
            return false;
        }
        $lmonth = $month;
        $lday = $day;

        // 获取查询日期到当年1月1日的天数
        $date1 = strtotime($year . "-01-01");
        $date2 = strtotime($year . "-" . $month . "-" . $day);
        $days = round(($date2 - $date1) / 3600 / 24);
        $days += 1;

        // 获取相应年度农历数据，化成数组Larray
        $larray = self::$ln_day[$year - self::$ln_start];
        if ($days <= $larray[0]) {
            $lyear = $year - 1;
            $days = $larray[0] - $days;
            $larray = self::$ln_day[$lyear - self::$ln_start];
            if ($days < $larray[12]) {
                $lmonth = 12;
                $lday = $larray[12] - $days;
            } else {
                $lmonth = 11;
                $days = $days - $larray[12];
                $lday = $larray[11] - $days;
            }
        } else {
            $lyear = $year;
            $days = $days - $larray[0];
            for ($i = 1; $i <= 12; $i++) {
                if ($days > $larray[$i]) {
                    $days = $days - $larray[$i];
                } else {
                    if ($days > 30) {
                        $days = $days - $larray[13];
                    }

                    $lmonth = $i;
                    $lday = $days;
                    break;
                }
            }
        }

        if ($time) {
            return mktime(0, 0, 0, $lmonth, $lday, $lyear);
        } else {
            return self::LYearName($lyear)."年".self::LMonName($lmonth)."月".self::LDayName($lday);
        }
    }

    /**
     * 公历该月的天数
     *
     * @param int|string $year  年
     * @param int|string $month 月
     * @return int
     */
    private static function getSMon($year, $month)
    {
        if (self::isLeapYear($year) && $month == 2) {
            return 29;
        } else {
            return self::$gg_day[$month];
        }
    }

    /**
     * 是否润年
     *
     * @param int|string $year 年
     * @return bool
     */
    private static function isLeapYear($year)
    {
        return ($year % 4 == 0) && (($year % 100 != 0) || ($year % 400 == 0));
    }

    /**
     * 农历年份转换
     *
     * @param int|string $year 年
     * @return string
     */
    private static function LYearName($year)
    {
        $year = (string) $year;
        $name = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
        $tmp = '';
        for ($i = 0; $i < 4; $i++) {
            $tmp .= $name[$year[$i]];
        }

        return $tmp;
    }

    //公历转农历(Sdate：公历日期)

    /**
     * 农历月份转换
     *
     * @param int|string $month 月
     * @return mixed
     */
    private static function LMonName($month)
    {
        if ($month >= 1 && $month <= 12) {
            $Name = [1 => "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"];

            return $Name[$month];
        }

        return $month;
    }

    //农历转公历(date：农历日期； type：是否闰月)

    /**
     * 农历日转换
     *
     * @param int|string $day 日
     * @return mixed
     */
    private static function LDayName($day)
    {
        if ($day >= 1 && $day <= 30) {
            $name = [
                1 => "初一",
                "初二",
                "初三",
                "初四",
                "初五",
                "初六",
                "初七",
                "初八",
                "初九",
                "初十",
                "十一",
                "十二",
                "十三",
                "十四",
                "十五",
                "十六",
                "十七",
                "十八",
                "十九",
                "二十",
                "廿一",
                "廿二",
                "廿三",
                "廿四",
                "廿五",
                "廿六",
                "廿七",
                "廿八",
                "廿九",
                "三十"
            ];

            return $name[$day];
        }

        return $day;
    }
}